package com.louis.util;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

public class FieldUtil {

	private static Logger logger = Logger.getLogger(FieldUtil.class);

	private static Map<Class<?>, Map<String, Field>> classMappingField = new HashMap<Class<?>, Map<String, Field>>();

	public static Field getDeclaredField(Class<?> clazz, String fieldName) {
		Field field = getFieldFromCache(clazz, fieldName);
		if (field == null) {
			try {
				field = clazz.getDeclaredField(fieldName);
				setFieldToCache(clazz, field);
			} catch (SecurityException e) {
				logger.warn(e.getMessage());
			} catch (NoSuchFieldException e) {
				logger.warn(e.getMessage());
			}
		}
		return field;
	}

	public static Collection<Field> getDeclaredFields(Class<?> clazz, boolean superclass) {
		Class<?> currentClazz = clazz;
		Collection<Field> fieldFromCache = getFieldFromCache(currentClazz, superclass);
		if (fieldFromCache.isEmpty()) {
			while (!clazz.equals(Object.class)) {
				Field[] declaredFields = getDeclaredFields(clazz);
				setFieldToCache(clazz, declaredFields);
				clazz = clazz.getSuperclass();
			}
		}
		return getFieldFromCache(currentClazz, superclass);
	}

	private static Field[] getDeclaredFields(Class<?> clazz) {
		return clazz.getDeclaredFields();
	}

	private static void setFieldToCache(Class<?> clazz, Field[] fields) {
		for (Field field : fields) {
			setFieldToCache(clazz, field);
		}
	}

	private static Collection<Field> getFieldFromCache(Class<?> clazz, boolean superclass) {
		Collection<Field> fieldFromCache = getFieldFromCache(clazz);
		if (superclass) {
			List<Field> list = new ArrayList<Field>();
			list.addAll(fieldFromCache);
			clazz = clazz.getSuperclass();
			while (clazz != null && !clazz.equals(Object.class)) {
				list.addAll(0, getFieldFromCache(clazz));
				clazz = clazz.getSuperclass();
			}
			return list;
		} else {
			return fieldFromCache;
		}
	}

	private static Collection<Field> getFieldFromCache(Class<?> clazz) {
		Map<String, Field> map = classMappingField.get(clazz);
		if (map == null) {
			map = new HashMap<String, Field>();
		}
		return map.values();
	}

	private static Field getFieldFromCache(Class<?> clazz, String fieldName) {
		Map<String, Field> map = classMappingField.get(clazz);
		if (map != null) {
			return map.get(fieldName);
		}
		return null;
	}

	private static void setFieldToCache(Class<?> clazz, Field field) {
		Map<String, Field> fieldMap = classMappingField.get(clazz);
		if (fieldMap == null) {
			fieldMap = new HashMap<String, Field>();
			classMappingField.put(clazz, fieldMap);
		}
		Field fieldCache = fieldMap.get(field.getName());
		if (fieldCache == null) {
			fieldMap.put(field.getName(), field);
		}
	}
}
